/*
 * @Author      : ZhouQiJun
 * @Date        : 2024-10-26 23:11:07
 * @LastEditors : ZhouQiJun
 * @LastEditTime: 2025-08-21 05:19:50
 * @Description : 关于博主，前端程序员，最近专注于 webGIS 开发
 * @加微信         : MasonChou123，进技术交流群
 */
import { isGeoJson, isWKT } from '#ol-vue-hooks'
import { isNil } from 'es-toolkit'
import { Collection, Feature } from 'ol'
import { isNull, randomStr, repeatRun } from 'petite-utils'

import { GeoJSON, WKT } from 'ol/format'
import { Polygon } from 'ol/geom'
import { DragZoom, Draw, Extent, Interaction, Modify, Select, Snap, Translate } from 'ol/interaction'
import VectorLayer from 'ol/layer/Vector'
import VectorSource from 'ol/source/Vector'
import { Circle, Fill, Stroke, Style } from 'ol/style'

const olBrandColor = getComputedStyle(document.documentElement).getPropertyValue('--ol-foreground-color') //?? '#00AAFF'
const buttons = [
  {
    name: 'point',
    type: 'Point',
    title: '绘制绘制点',
    icon: (color) => {
      return `<svg
  xmlns="http://www.w3.org/2000/svg"
  width="1.5rem"
  height="1.5rem"
  viewBox="0 0 24 24" title="绘制点">
  <path
    fill="${color}"
    stroke="${color}"
    d="M12 6.5A2.5 2.5 0 0 1 14.5 9a2.5 2.5 0 0 1-2.5 2.5A2.5 2.5 0 0 1 9.5 9A2.5 2.5 0 0 1 12 6.5M12 2a7 7 0 0 1 7 7c0 5.25-7 13-7 13S5 14.25 5 9a7 7 0 0 1 7-7m0 2a5 5 0 0 0-5 5c0 1 0 3 5 9.71C17 12 17 10 17 9a5 5 0 0 0-5-5" />
</svg>`
    },
  },
  {
    name: 'line',
    type: 'LineString',
    title: '绘制绘制线',
    icon: (color) => {
      return `<svg
xmlns="http://www.w3.org/2000/svg"
width="1.5rem"
height="1.5rem"
viewBox="0 0 24 24">
<path
  fill="${color}"
  stroke="${color}"
  stroke-linecap="round"
  stroke-linejoin="round"
  stroke-width="1"
  d="M4 18a2 2 0 1 0 4 0a2 2 0 1 0-4 0M16 6a2 2 0 1 0 4 0a2 2 0 1 0-4 0M7.5 16.5l9-9" />
</svg>`
    },
  },
  {
    name: 'polygon',
    type: 'Polygon',
    title: '绘制多边形',
    icon: (color) => {
      return `<svg
      xmlns="http://www.w3.org/2000/svg"
      width="1.5rem"
      height="1.5rem"
      viewBox="0 0 24 24">
      <path
        fill="${color}"
        stroke-width="1"
        d="m21.934 11.751l-4.75-8.255a.5.5 0 0 0-.434-.25h-9.5a.5.5 0 0 0-.434.25l-4.75 8.255a.5.5 0 0 0 0 .498l4.75 8.255c.09.155.255.25.434.25h9.5a.5.5 0 0 0 .434-.25l4.75-8.255a.5.5 0 0 0 0-.498m-5.473 8.004H7.539L3.077 12L7.54 4.245h8.922L20.923 12z" />
    </svg>`
    },
  },
  {
    name: 'circle',
    type: 'Circle',
    title: '绘制圆',
    icon: (color) => {
      return `<svg
    xmlns="http://www.w3.org/2000/svg"
    width="1.5rem"
    height="1.5rem"
    viewBox="0 0 24 24">
    <path
      fill="none"
      stroke="${color}"
      stroke-linecap="round"
      stroke-linejoin="round"
      stroke-width="1"
      d="M3 12c0 -4.97 4.03 -9 9 -9c4.97 0 9 4.03 9 9c0 4.97 -4.03 9 -9 9c-4.97 0 -9 -4.03 -9 -9Z" />
  </svg>`
    },
  },
  {
    name: 'rectangle',
    type: 'Rectangle',
    title: '绘制矩形',
    icon: (color) => {
      return `<svg
       xmlns="http://www.w3.org/2000/svg"
       width="1.5rem"
       height="1.5rem"
       viewBox="0 0 24 24">
       <path
         fill="none"
         stroke="${color}"
         stroke-linecap="round"
         stroke-linejoin="round"
         stroke-width="1"
         d="M3 7a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
     </svg>`
    },
  },
  {
    name: 'freehand',
    type: 'freehand',
    title: '自由绘制，只有线和多边形支持',
    icon: (color) => {
      return `<svg
      xmlns="http://www.w3.org/2000/svg"
      width="1.5rem"
      height="1.5rem"
      viewBox="0 0 32 32">
      <path
        fill="${color}"
        d="M14 2.5a.5.5 0 0 1 .5-.5h1c4.01 0 6.81.25 8.621.702c.903.226 1.602.512 2.085.875c.497.372.794.852.794 1.423c0 .47-.122.948-.52 1.368c-.379.399-.963.696-1.783.926C23.062 7.752 20.25 8 15.5 8s-7.438.252-8.928.669c-.742.208-1.127.441-1.326.652C5.066 9.51 5 9.72 5 10c0 .179.078.387.394.623c.33.247.88.493 1.727.704c1.232.308 3.002.525 5.417.62q-.038.27-.038.553v.446c-2.456-.096-4.301-.318-5.621-.648c-.903-.226-1.602-.512-2.085-.875C4.297 11.05 4 10.571 4 10c0-.47.122-.948.52-1.368c.379-.399.963-.696 1.783-.926C7.939 7.248 10.75 7 15.5 7c4.751 0 7.439-.252 8.928-.669c.742-.208 1.127-.441 1.326-.652c.18-.19.246-.399.246-.679c0-.179-.078-.387-.394-.623c-.33-.247-.88-.493-1.727-.704C22.189 3.25 19.489 3 15.5 3h-1a.5.5 0 0 1-.5-.5m2.476 7.5a2.5 2.5 0 0 0-2.5 2.5v6.731l-1.285-.57a4.49 4.49 0 0 0-5.513 1.55a1.006 1.006 0 0 0 .427 1.491c5.713 2.518 7.633 4.811 8.437 6.54c.502 1.079 1.616 1.93 2.917 1.73l3.634-.56a2.5 2.5 0 0 0 2.046-1.867l1.224-4.92a4.5 4.5 0 0 0-3.651-5.53l-3.237-.521v-4.075A2.5 2.5 0 0 0 16.476 10m-1.5 2.5a1.5 1.5 0 0 1 3 0V17a.5.5 0 0 0 .42.494l3.657.588a3.5 3.5 0 0 1 2.84 4.301l-1.224 4.92a1.5 1.5 0 0 1-1.228 1.121l-3.634.56c-.742.114-1.488-.368-1.858-1.163c-.944-2.03-3.104-4.462-8.941-7.034l-.004-.002l-.002-.005a3.49 3.49 0 0 1 4.284-1.204l1.988.881a.5.5 0 0 0 .703-.457z" />
    </svg>`
    },
  },
  {
    name: 'modify',
    type: 'modify',
    title: '修改要素，按住 alt 可删除顶点',
    icon: (color) => {
      return `<svg
      xmlns="http://www.w3.org/2000/svg"
      width="1.5rem"
      height="1.5rem"
      viewBox="0 0 24 24">
      <g
        fill="none"
        stroke="${color}"
        stroke-linecap="round"
        stroke-linejoin="round"
        stroke-width="1.1">
        <path
          d="m16.475 5.408l2.117 2.117m-.756-3.982L12.109 9.27a2.1 2.1 0 0 0-.58 1.082L11 13l2.648-.53c.41-.082.786-.283 1.082-.579l5.727-5.727a1.853 1.853 0 1 0-2.621-2.621" />
        <path d="M19 15v3a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h3" />
      </g>
    </svg>`
    },
  },
  {
    name: 'move',
    type: 'move',
    title: '移动要素',
    icon: (color) => {
      return `<svg
      xmlns="http://www.w3.org/2000/svg"
      width="1.5rem"
      height="1.5rem"
      viewBox="0 0 24 24">
      <path
        fill="${color}"
        d="m12 3l.365-.342l-.365-.39l-.365.39zm-.5 6a.5.5 0 0 0 1 0zm3.865-3.142l-3-3.2l-.73.684l3 3.2zm-3.73-3.2l-3 3.2l.73.684l3-3.2zM11.5 3v6h1V3zm9.5 9l.342.365l.39-.365l-.39-.365zm-6-.5a.5.5 0 0 0 0 1zm3.142 3.865l3.2-3l-.684-.73l-3.2 3zm3.2-3.73l-3.2-3l-.684.73l3.2 3zM21 11.5h-6v1h6zM12 21l.365.342l-.365.39l-.365-.39zm-.5-6a.5.5 0 0 1 1 0zm3.865 3.142l-3 3.2l-.73-.684l3-3.2zm-3.73 3.2l-3-3.2l.73-.684l3 3.2zM11.5 21v-6h1v6zM3 12l-.342.365l-.39-.365l.39-.365zm6-.5a.5.5 0 0 1 0 1zm-3.142 3.865l-3.2-3l.684-.73l3.2 3zm-3.2-3.73l3.2-3l.684.73l-3.2 3zM3 11.5h6v1H3z" />
    </svg>`
    },
  },
  // {
  //   name: 'save',
  //   title: '保存修改',
  //   icon: color => {
  //     return `<svg
  //     xmlns="http://www.w3.org/2000/svg"
  //     width="1.5rem"
  //     height="1.5rem"
  //     viewBox="0 0 24 24">
  //     <g fill="none" fill-rule="evenodd">
  //       <path
  //         d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
  //       <path
  //         fill="${color}"
  //         d="M6 2a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V6.414A2 2 0 0 0 19.414 5L17 2.586A2 2 0 0 0 15.586 2zm0 2h9.586L18 6.414V20H6zm10.238 6.793a1 1 0 1 0-1.414-1.414l-4.242 4.243l-1.415-1.415a1 1 0 0 0-1.414 1.414l2.05 2.051a1.1 1.1 0 0 0 1.556 0l4.88-4.879Z" />
  //     </g>
  //   </svg>`
  //   },
  // },
  {
    name: 'delete',
    type: 'delete',
    title: '删除要素',
    icon: (color) => {
      return `<svg
      xmlns="http://www.w3.org/2000/svg"
      width="1.5rem"
      height="1.5rem"
      viewBox="0 0 20 20">
      <path
        fill="${color}"
        d="M8.5 4h3a1.5 1.5 0 0 0-3 0m-1 0a2.5 2.5 0 0 1 5 0h5a.5.5 0 0 1 0 1h-1.054l-1.194 10.344A3 3 0 0 1 12.272 18H7.728a3 3 0 0 1-2.98-2.656L3.554 5H2.5a.5.5 0 0 1 0-1zM5.741 15.23A2 2 0 0 0 7.728 17h4.544a2 2 0 0 0 1.987-1.77L15.439 5H4.561zM8.5 7.5A.5.5 0 0 1 9 8v6a.5.5 0 0 1-1 0V8a.5.5 0 0 1 .5-.5M12 8a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0z" />
    </svg>`
    },
  },
]

let draw = null
let drawBbox = null
let bboxFeature = null
let modify = null
let select = null
let snap = null
let translate = null
let drawType = null
const enableDrawTypes = ['Point', 'LineString', 'Polygon', 'Circle']
const allFeatures = new Collection()
export default class OlDraw extends Interaction {
  /**
   * Interaction 交互基类
   * 常用的属性： this.map_
   * 常用的方法： this.getMap()、this.setMap()、 this.getActive()、this.setActive()
   */
  /**
   * @type {null}
   */
  source = null
  layer = null
  hasAddedLayer = false
  actions_ = ['modify', 'translate', 'delete']
  geomTypes_ = ['Point', 'LineString', 'Circle', 'Polygon']
  freehand_ = false
  showDrawBar_ = true
  constructor(options = {}) {
    // 创建一个按钮元素
    super({
      handleEvent: () => {
        return true
      },
      ...options,
    })

    this.freehand_ = options?.freehand !== false
    this.showDrawBar_ = options?.showDrawBar !== false

    if (!isNil(options?.actions)) {
      this.actions_ = options.actions
    }
    if (Array.isArray(options.geomTypes)) {
      this.geomTypes_ = options.geomTypes
    }
    const addDrawBar = (times, stop) => {
      this.map_ = this.getMap()
      // console.log('addDrawBar', this.getMap())
      if (this.map_) {
        stop()
        if (this.showDrawBar_) {
          this.appendBtnContainer(this.map_)
        }
      }
    }
    repeatRun(addDrawBar)
  }
  appendBtnContainer(map) {
    const btnContainer = document.createElement('div')
    btnContainer.classList.add('ol-draw-container')
    btnContainer.classList.add('ol-control')
    btnContainer.style = 'pointer-events: auto;'

    const controlContainer = map.getTargetElement().querySelector('.ol-overlaycontainer-stopevent')
    const _types = buttons.filter((btn) => this.geomTypes_.includes(btn.type))
    if (this.freehand_) {
      _types.push(buttons.find((btn) => btn.name === 'freehand'))
    }
    const _actions = buttons.filter((btn) => this.actions_.includes(btn.name))
    const _buttons = [..._types, ..._actions]
    _buttons.forEach((btn) => {
      const btnElement = document.createElement('button')
      btnElement.classList.add('ol-draw-btn')
      btnElement.innerHTML = btn.icon(olBrandColor)
      btnElement.title = btn.title
      btnContainer.appendChild(btnElement)
      btnElement.addEventListener('click', (e) => {
        if (!this.layer) {
          this.addLayer(map)
        }
        const target = e.target.closest('button')
        const classList = target.classList
        classList.add('active')
        disableOtherButtons(target)
        console.log(btn.type)
        const isSimpleGeometry = enableDrawTypes.includes(btn.type)
        const isBbox = 'Rectangle' === btn.type
        if (isBbox) {
          this.enableDragZoom(false)
          // BUG
          this.enableDrawRectangle(map)
          return true
        }
        this.enableDragZoom(true)
        if (bboxFeature) {
          this.appendBboxFeature()
        }
        if (isSimpleGeometry) {
          drawType = btn.type
          this.enableDrawSimpleGeometry(btn.type)
          return true
        }
        if (btn.type === 'freehand') {
          // 只有 LineString 和 Polygon 能开启自由绘制 Point 和 Circle 无法开启
          const type = ['LineString', 'Polygon'].includes(drawType) ? drawType : 'LineString'
          drawType = type
          this.enableDrawSimpleGeometry(drawType, true)
          return true
        }
        if (btn.type === 'move') {
          this.enableTranslate()
          return true
        }
        if (btn.type === 'modify') {
          this.enableModify()
          return true
        }
        if (btn.type === 'delete') {
          this.enableDelete()
          return true
        }
      })
    })
    controlContainer.appendChild(btnContainer)
  }
  enableDrawRectangle(map) {
    this.disableDrawSimpleGeometry()
    this.disableModify()
    this.disableTranslate()
    this.disableSelect()
    if (drawBbox != null) {
      drawBbox = new Extent({
        boxStyle: new Style({
          fill: new Fill({
            color: olBrandColor + '55',
          }),
        }),
        pointerStyle: new Style({
          image: new Circle({
            radius: 6, // 默认是 6
            fill: new Fill({
              color: [0, 153, 255, 1],
            }),
            stroke: new Stroke({
              color: '#fff', // 默认是 white
              width: 1.5, // 默认是 1.5
            }),
          }),
        }),
        pixelTolerance: 10, // 默认是 10
      })
      map.addInteraction(drawBbox)
    } else {
      drawBbox.setActive(true)
    }
    const debounce = () => {
      let timer = null
      return (event) => {
        if (!Array.isArray(event?.extent)) return true
        const [x1, y1, x2, y2] = event.extent
        const bottomLeft = [x1, y1]
        const topRight = [x2, y2]
        const bottomRight = [x2, y1]
        const topLeft = [x1, y2]
        const end = [x1, y1]
        const coordinates = [bottomLeft, bottomRight, topRight, topLeft, end]
        const polygon = new Polygon([coordinates])
        const id = genId()
        bboxFeature = new Feature({
          geometry: polygon,
        })
        bboxFeature.setId(id)
        bboxFeature.set('isBbox', true, true)
        if (timer) return true
        timer = setTimeout(() => {
          const array = allFeatures.getArray().filter((item) => {
            const isBbox = item.get('isBbox')
            if (isBbox) allFeatures.remove(item)
            return !isBbox
          })
          array.push(bboxFeature)
          allFeatures.extend(array)
          const geoJSON = new GeoJSON().writeFeaturesObject(array)
          this.drawComplete({ geoJSON, originType: 'extentchanged' })
          timer = null
        }, 500)
        return true
      }
    }
    drawBbox.on('extentchanged', debounce())
  }
  disableDrawRectangle() {
    drawBbox?.setActive(false)
  }
  enableDrawSimpleGeometry(type, freehand = false) {
    if (!type) return
    if (!this.layer) this.addLayer(this.map_)
    this.disableDrawRectangle()
    this.disableModify()
    this.disableTranslate()
    this.disableSelect()
    // 新建之前移除旧的
    this.map_.removeInteraction(draw)
    draw = new Draw({
      source: this.genSource(),
      type: type,
      freehand,
    })
    this.map_.addInteraction(draw)
    draw.on('drawend', (e) => {
      const feature = e.feature
      const id = genId()
      feature.setId(id)
      allFeatures.push(feature)
      const geoJSON = new GeoJSON().writeFeaturesObject(allFeatures.getArray())
      this.drawComplete({ geoJSON, originType: 'drawend' })
      this.drawEnd(e)
    })
  }
  disableDrawSimpleGeometry() {
    draw?.setActive(false)
  }
  enableTranslate() {
    this.disableDrawSimpleGeometry()
    this.disableModify()
    this.enableSelect()

    if (!translate) {
      translate = new Translate({
        features: select.getFeatures(),
      })
      this.map_.addInteraction(translate)
      translate.on('translateend', (e) => {
        // const { coordinate, startCoordinate, features, type } = e
        const geoJSON = toGeoJSON(allFeatures.getArray())
        this.drawComplete({ geoJSON, originType: 'translateend' })
      })
    } else {
      translate.setActive(true)
    }
  }
  disableTranslate() {
    translate?.setActive(false)
  }
  enableModify() {
    this.disableModify()
    this.disableTranslate()
    this.disableDrawRectangle()

    this.enableSelect()

    if (modify == null) {
      modify = new Modify({
        features: select.getFeatures(),
      })
      this.map_.addInteraction(modify)
    } else {
      modify.setActive(true)
    }
    modify.on('modifyend', (e) => {
      const { features } = e
      const idList = features.getArray().map((feat) => {
        return feat.getId()
      })
      const featArray = allFeatures.getArray().filter((feat) => {
        return !idList.includes(feat.getId())
      })
      allFeatures.clear()
      allFeatures.extend([...featArray, ...features.getArray()])
      const geoJSON = toGeoJSON(allFeatures.getArray())
      console.log('modifyend', idList, geoJSON)
      this.drawComplete({ geoJSON, originType: 'modifyend' })
    })
  }
  disableModify() {
    modify?.setActive(false)
  }
  enableSelect() {
    this.disableDrawSimpleGeometry()
    this.disableModify()
    this.disableDrawRectangle()
    this.disableDrawRectangle()
    if (select == null) {
      select = new Select({
        layers: [this.layer],
        style: new Style({
          fill: new Fill({
            color: '#f00',
          }),
          stroke: new Stroke({
            color: '#ffcc33',
            width: 3,
          }),
          image: new Circle({
            radius: 7,
            fill: new Fill({
              color: '#ffcc33',
            }),
          }),
        }),
        hitTolerance: 5,
      })
      this.map_.addInteraction(select)
    } else {
      select.setActive(true)
    }
  }
  disableSelect() {
    select?.setActive(false)
  }
  enableDelete() {
    this.enableSelect()
    const features = select.getFeatures()
    features.forEach((feature) => {
      const id = feature.getId()
      const hasRemove = !!allFeatures.remove(feature)
      if (hasRemove) {
        console.log('remove', id)
      }
      // 从数据源中移除
      this.source.removeFeature(feature)
    })
    const geoJSON = toGeoJSON(allFeatures.getArray())
    this.drawComplete({ geoJSON, originType: null })
  }
  enableDragZoom(enable = true) {
    this.map_.getInteractions().forEach((interaction) => {
      if (interaction instanceof DragZoom) {
        interaction.setActive(enable)
      }
    })
  }
  appendBboxFeature() {
    let cloneFeature = bboxFeature.clone()
    cloneFeature.setId(genId())
    cloneFeature.set('isBbox', false, true)
    source.addFeature(cloneFeature)
    bboxFeature = null
  }
  addLayer(map) {
    if (this.hasAddedLayer) return
    const size = map.getAllLayers().length
    const layer = this.genLayer(size + 10)
    map.addLayer(layer)
    this.hasAddedLayer = true
  }
  drawComplete({ geoJSON, originType }) {
    this.dispatchEvent({
      type: 'drawcomplete',
      geoJSON,
      originType,
    })
  }
  drawEnd(event) {
    this.dispatchEvent(event)
  }
  genSource() {
    if (this.source) return this.source
    this.source = new VectorSource()
    return this.source
  }
  genLayer(zIndex) {
    const layer = new VectorLayer({
      source: this.genSource(),
      zIndex,
      className: `ol-layer ol-draw-layer vector-layer z-index-${zIndex}`,
    })
    layer.set('name', 'ol-draw-layer')
    this.layer = layer
    return layer
  }
  addFeature(wktOrJson, options, id) {
    this.addLayer(this.map_)
    if (isWKT(wktOrJson)) {
      const feature = new WKT().readFeature(wktOrJson, options)
      allFeatures.push(feature)
      feature.setId(id)
      this.source.addFeature(feature)
    } else if (isGeoJson(wktOrJson)) {
      const feature = new GeoJSON().readFeature(wktOrJson, options)
      allFeatures.push(feature)
      if (isNull(id)) {
        feature.setId(id)
      }
      this.source.addFeature(feature)
    }
  }
}

function genId() {
  return randomStr(10, 10)
  // return 'ol-draw-feat_' + Math.random().toString(36).slice(2)
}

function toGeoJSON(features) {
  return new GeoJSON().writeFeaturesObject(features)
}

function disableOtherButtons(target) {
  const allDrawButtons = document.querySelector('.ol-draw-container')?.querySelectorAll('.ol-draw-btn')
  allDrawButtons?.forEach((btn) => {
    if (!target.contains(btn)) {
      btn.classList.remove('active')
    }
  })
}
